using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;
using System.Net;
using System.IO;

namespace SharpCast
{
    [ClassInterface(ClassInterfaceType.AutoDual)]
    public class DownloadHandle
    {
        public enum State { connecting, downloading, finished, cancelled, error };
        private Episode _episode;
        private State _state = State.connecting;
        private long _downloaded;
        private long _contentLength;
        private FileInfo output;

        private int _chunkSize = 4096;

        private Thread thread;
        private bool cancelled = false;

        public delegate void chunkDownloadedDelegate();
        public event chunkDownloadedDelegate chunkDownloaded;

        public delegate void stateChangedDelegate(State state);
        public event stateChangedDelegate stateChanged;

        public DownloadHandle(Episode episode, FileInfo output)
        {
            this._episode = episode;
            this.output = output;
        }

        private void download() {
            // Create request
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(episode.url);
            request.UserAgent = "SharpCast 0.1";

            long remainingLength = _episode.fileSize;

            // Check if file exists
            if (output.Exists)
            {
                remainingLength -= output.Length;
                if (remainingLength == 0)
                {
                    _state = State.finished;
                    if (stateChanged != null)
                        stateChanged(_state);
                    return;
                }
                request.AddRange((int) output.Length, (int) _episode.fileSize - 1);
            }
            FileStream outputStream;

            // Connect to server
            WebResponse response = null;
            try
            {
                response = request.GetResponse();
            }
            catch
            {
                request = (HttpWebRequest)HttpWebRequest.Create(episode.url);
                request.UserAgent = "SharpCast 0.1";
                response = request.GetResponse();
            }

            if (remainingLength > 0 && response.ContentLength == remainingLength) // Resume supported
                outputStream = new FileStream(output.ToString(), FileMode.Append, FileAccess.Write);
            else
                outputStream = new FileStream(output.ToString(), FileMode.Create, FileAccess.Write);

            _contentLength = response.ContentLength;
            _downloaded = output.Exists ? output.Length : 0;

            Stream stream = response.GetResponseStream();

            // Update state
            _state = State.downloading;
            if (stateChanged != null)
                stateChanged(_state);

            byte[] buffer = new byte[_chunkSize];
            int numBytes = 0;
            do
            {
                numBytes = stream.Read(buffer, 0, buffer.Length);
                outputStream.Write(buffer, 0, numBytes);
                _downloaded += numBytes;
                chunkDownloaded();
            }
            while (numBytes > 0 && !cancelled);

            outputStream.Close();
            if (cancelled)
                _state = State.cancelled;
            else
                _state = State.finished;

            if (stateChanged != null)
                stateChanged(_state);
        }

        public void cancel()
        {
            cancelled = true;
        }

        public long contentLength
        {
            get { return _contentLength; }
        }

        public long downloaded
        {
            get { return _downloaded; }
        }

        public State state
        {
            get { return _state; }
        }

        public Episode episode
        {
            get { return _episode; }
        }

        public int chunkSize
        {
            get { return _chunkSize; }
        }

        public void start()
        {
            ThreadStart job = new ThreadStart(download);
            thread = new Thread(job);
            thread.Start();
        }
    }
}
